﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Unity;

namespace PB.Airsoft.Core.Unity
{
    /// <summary>
    /// Extension class for the <see cref="UnityContainer"/> that tracks types being registered.
    /// </summary>
    internal class TypeTrackingExtension : UnityContainerExtension
    {
        public const string DefaultName = "__Default";

        private readonly Dictionary<Type, Dictionary<string, Type>> registeredTypes = new Dictionary<Type, Dictionary<string, Type>>();
        private readonly Dictionary<Type, HashSet<string>> registeredInstances = new Dictionary<Type, HashSet<string>>();

        protected override void Initialize()
        {
            Context.Registering += Context_Registering;
            Context.RegisteringInstance += Context_RegisteringInstance;
        }

        /// <summary>
        /// Handles the Registering event of the Context object.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.Practices.Unity.RegisterEventArgs"/> instance containing the event data.</param>
        private void Context_Registering(object sender, RegisterEventArgs e)
        {
            Dictionary<string, Type> types;
            if (!registeredTypes.TryGetValue(e.TypeFrom, out types))
            {
                types = new Dictionary<string, Type>();
                types[e.Name ?? DefaultName] = e.TypeTo;
                registeredTypes.Add(e.TypeFrom, types);
            }
            else
            {
                types[e.Name ?? DefaultName] = e.TypeTo;
            }
        }

        /// <summary>
        /// Handles the RegisteringInstance event of the Context object.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.Practices.Unity.RegisterInstanceEventArgs"/> instance containing the event data.</param>
        private void Context_RegisteringInstance(object sender, RegisterInstanceEventArgs e)
        {
            HashSet<string> instances;
            if (!registeredInstances.TryGetValue(e.RegisteredType, out instances))
            {
                instances = new HashSet<string>() { e.Name ?? DefaultName };
                registeredInstances.Add(e.RegisteredType, instances);
            }
            else
            {
                string name = e.Name ?? DefaultName;
                instances.Remove(name);
                instances.Add(name);
            }
        }

        /// <summary>
        /// Gets the named type registered for a base type.
        /// </summary>
        /// <param name="type">The base type.</param>
        /// <param name="name">The registration name.</param>
        /// <returns></returns>
        public Type GetRegisteredType(Type type, string name)
        {
            Dictionary<string, Type> types;
            if (registeredTypes.TryGetValue(type, out types))
            {
                return types[name];
            }
            return null;
        }

        /// <summary>
        /// Determines whether there is a registered type for the specified type.
        /// </summary>
        /// <param name="type">The base type.</param>
        /// <param name="name">The registration name.</param>
        /// <returns>
        /// <see langword="true"/> if there is a registered type for the specified type; otherwise, <see langword="false"/>.
        /// </returns>
        public bool IsRegisteredType(Type type, string name)
        {
            Dictionary<string, Type> types;
            return registeredTypes.TryGetValue(type, out types) && types.ContainsKey(name);
        }

        /// <summary>
        /// Determines whether there is a registered singleton instance for the specified type.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="name">The name.</param>
        /// <returns>
        /// <see langword="true"/> if there is a registered singleton instance for the specified type; otherwise, <see langword="false"/>.
        /// </returns>
        public bool IsRegisteredInstance(Type type, string name)
        {
            HashSet<string> instances;
            return registeredInstances.TryGetValue(type, out instances) && instances.Contains(name);
        }
    }

}
